﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Vector3 = DDD.Vector3<float>;
using Vector4 = DDD.Vector4<float>;


namespace DDD {

    /// <summary>
    /// シーンのノード
    /// </summary>
    /// <remarks>
    /// <see cref="Node"/>は<see cref="Attachable"/>を継承してノード間の親子関係を追加します。
    /// このクラスはシーングラフの構成するとともに階層構造に応じた処理を担当します。
    /// </remarks>
    public class Node : Attachable {

        #region Field
        Node parent;
        List<Node> nodes;
        BoundingBox bbox;
        BoundingSphere bsph;
        CollisionShape cshp;
        #endregion

        #region Constructor
        /// <summary>
        ///  コンストラクター
        /// </summary>
        public Node () {
            this.parent = null;
            this.nodes = new List<Node> ();
            this.bbox = new BoundingBox ();
            this.bsph = new BoundingSphere ();
            this.cshp = new CollisionShape ();
        }
        #endregion


        #region Property
        /// <summary>
        /// 子ノードの総数
        /// </summary>
        public int ChildCount {
            get { return nodes.Count; }
        }

        /// <summary>
        /// 親ノード
        /// </summary>
        public Node Parent {
            get { return parent; }
        }

        /// <summary>
        /// 全ての子ノードを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// この列挙子はこのノードの持つ直接の子ノードを列挙します。
        /// 子ノードのさらに子ノードは列挙しません。
        /// </remarks>
        public IEnumerable<Node> Children {
            get { return nodes; }
        }

        /// <summary>
        /// フリーズフラグ
        /// </summary>
        /// <remarks>
        /// このノードを含むシーンがフリーズされた事を示すフラグを返します。
        /// シーンに所属していない時はfalseが返ります。
        /// </remarks>
        public bool IsFreezed {
            get {
                if (Root == null) {
                    return false;
                }
                return Root.IsFreezed;
            }
        }

        /// <summary>
        /// 実効バウンディングボックス
        /// </summary>
        /// <remarks>
        /// 階層構造を考慮した実行バウンディングボックスを返します。
        /// フリーズしたシーンでのみ有効です。
        /// </remarks>
        public BoundingBox BoundingBox {
            get {
                return bbox;
            }
        }

        /// <summary>
        /// 実効バウンディングスフィア
        /// </summary>
        /// <remarks>
        /// 階層構造を考慮した実行バウンディングスフィアを返します。
        /// フリーズしたシーンでのみ有効です。
        /// </remarks>
        public BoundingSphere BoundingSphere {
            get { return bsph; }
        }

        /// <summary>
        /// 実効コリジョン形状
        /// </summary>
        /// <remarks>
        /// 階層構造を考慮した実効コリジョン形状を返します。
        /// フリーズしたシーンでのみ有効です。
        /// </remarks>
        public CollisionShape CollisionShape {
            get { return cshp; }
        }

        /// <summary>
        /// このノードと再帰的に得られたすべての子ノードを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// このノードとこのノードから再帰的に辿れる全ての子ノードを幅優先で列挙します。
        /// ノードの並び順は先頭がこのノードで子ノード、孫ノード、ひ孫ノードと続きます。
        /// (実装メモ)とりあえず幅優先で実装してあるがこれは有用か？
        /// </remarks>
        public IEnumerable<Node> Downwards {
            get {
                var downs = new List<Node> () { this };
                var nodes = this.nodes;
                while (nodes.Count > 0) {
                    downs.AddRange (nodes);
                    var tmp = new List<Node> ();
                    foreach (var n in nodes) {
                        tmp.AddRange (n.nodes);
                    }
                    nodes = tmp;
                }

                return downs;
            }
        }

        /// <summary>
        /// 再帰的に得られたすべての親ノードを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// このノードとこのノードから再帰的に辿れる全ての親ノードをこのノードから近い順に列挙します。
        /// ノードの並び順は先頭がこのノードで親ノード、祖父ノードと続きます。
        /// </remarks>
        public IEnumerable<Node> Upwards {
            get {
                var ups = new List<Node> () { this };
                var node = this.parent;
                while (node != null) {
                    ups.Add (node);
                    node = node.parent;
                }
                return ups;
            }
        }

        /// <summary>
        /// ノードの実効α
        /// </summary>
        /// <remarks>
        /// レンダリングで使用されるαはこのノードとルートノードまでの全てのαをかけ算した"実効"値です。
        /// このプロパティは実効ノードαを返します。
        /// </remarks>
        public float EffectiveAlpha {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("Not in the Scene");
                }
                return Upwards.Aggregate (1.0f, (s, node) => s * node.Alpha);
            }
        }

        /// <summary>
        /// 実効レンダリングフラグ
        /// </summary>
        /// <remarks>
        /// このノードがレンダリングされるにはこのノードとルートノードまでの全ての<see cref="Attachable.IsRenderingEnabled"/>が
        /// <dic>true</dic>になっている必要があります。
        /// </remarks>
        public bool IsEffectiveRenderingEnabled {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("Not in the Scene");
                }
                return Upwards.Aggregate (true, (s, node) => s & node.IsRenderingEnabled);
            }
        }

        /// <summary>
        /// 実効ピッキングフラグ
        /// </summary>
        /// <remarks>
        /// このノードがピッキングの対象になるにはこのノードとルートノードまでの全ての<see cref="Attachable.IsPickingEnabled"/>が
        /// <dic>true</dic>になっている必要があります。
        /// </remarks>
        public bool IsEffectivePickingEnabled {
            get {
                return Upwards.Aggregate (true, (s, node) => s & node.IsPickingEnabled);
            }
        }

        /// <summary>
        /// 実効コリジョンフラグ
        /// </summary>
        /// <remarks>
        /// このノードがコリジョン検出の対象になるにはこのノードとルートノードまでの全ての<see cref="Attachable.IsCollisionEnabled"/>が
        /// <dic>true</dic>になっている必要があります。
        /// </remarks>
        public bool IsEffectiveCollisionEnabled {
            get {
                return Upwards.Aggregate (true, (s, node) => s & node.IsCollisionEnabled);
            }
        }

        /// <summary>
        /// 実効LOD選択フラグ
        /// </summary>
        /// <remarks>
        /// このノードがLOD選択の対象になるにはこのノードとルートノードまでの全ての<see cref="Attachable.IsLODEnabled"/>が
        /// <dic>true</dic>になっている必要があります。
        /// </remarks>
        public bool IsEffectiveLODEnabled {
            get {
                return Upwards.Aggregate (true, (s, node) => s & node.IsLODEnabled);
            }
        }

        /// <summary>
        /// 最上位の親ノード(必然的に<see cref="World"/>)
        /// </summary>
        /// <remarks>
        /// 最上位のルートノードを返します。まだシーンに所属してない場合はnullを返します。
        /// <see cref="World"/>オブジェクトの<see cref="Root"/>プロパティは自分自身を返します。
        /// </remarks>
        public World Root {
            get {
                return Upwards.Last () as World;
            }
        }

        /// <summary>
        /// ペアレント座標系での位置(x,y,z)
        /// </summary>
        public Vector3 ParentPosition {
            get {
                if (parent == null) {
                    throw new InvalidOperationException ("Parent is null");
                }
                var pos = LocalToParent * Vector4.Zero;
                return pos.DividedByW ();
            }
        }

        /// <summary>
        /// ワールド座標系での位置(x,y,z)
        /// </summary>
        /// <remarks>
        /// このプロパティはシーンに所属している時のみ使用可能です
        /// </remarks>
        public Vector3 WorldPosition {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("Root is null");
                }
                var pos = LocalToWorld * Vector4.Zero;
                return pos.DividedByW ();
            }
        }

        /// <summary>
        /// カメラ座標系での位置(x,y,z)
        /// </summary>
        /// <remarks>
        /// このプロパティはシーンに所属していて、アクティブカメラがセットされている時のみ使用可能です
        /// </remarks>
        public Vector3 CameraPosition {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("This node is not in the Scene");
                }
                if (Root.ActiveCamera == null) {
                    throw new InvalidOperationException ("Active camera is null");
                }
                var pos = LocalToCamera * Vector4.Zero;
                return pos.DividedByW ();
            }
        }

        /// <summary>
        /// ワールド座標系での回転(quaternion)
        /// </summary>
        /// <remarks>
        /// このプロパティはシーンに所属している時だけ有効です
        /// </remarks>
        public Quaternion WorldOrientation {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("Not in the Scene");
                }
                return new Quaternion (LocalToWorld.Matrix3x3);
            }
        }


        /// <summary>
        /// カメラからの距離(ビュー座標)
        /// </summary>
        /// <remarks>
        /// このノードのカメラからの距離(ビュー座標)を返します。
        /// シーンに所属し<see cref="World.ActiveCamera"/>に有効なカメラがセットされているときのみ有効です。
        /// </remarks>
        public float DistanceFromCamera {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("This node is not in the Scene");
                }
                if (Root.ActiveCamera == null) {
                    throw new InvalidOperationException ("Active camera is not set");
                }
                var pos = LocalToCamera * Vector4.Zero;

                return pos.DividedByW ().Length;
            }
        }

        /// <summary>
        /// ペアレント座標系変換行列
        /// </summary>
        /// <remarks>
        /// このノードのローカル座標系からペアレント座標系に変換する変換行列です。
        /// </remarks>
        public Matrix4x4 LocalToParent {
            get {
                if (parent == null) {
                    throw new InvalidOperationException ("Parent is null");
                }
                return GetTransformTo (parent);
            }
        }

        /// <summary>
        /// ペアレント座標系変換行列(法線用)
        /// </summary>
        /// <remarks>
        /// このノードのローカル座標系からペアレント座標系に変換する変換行列です。
        /// </remarks>
        public Matrix3x3 LocalToParent3x3 {
            get {
                if (parent == null) {
                    throw new InvalidOperationException ("Parent is null");
                }
                return LocalToParent.Matrix3x3.Inverse ().Transpose ();
            }
        }

        /// <summary>
        /// ワールド座標変換行列
        /// </summary>
        /// <remarks>
        /// このノードのローカル座標系からワールド座標系に変換する変換行列です。
        /// このプロパティはノードがシーンに所属しているときだけ有効です。
        /// </remarks>
        public Matrix4x4 LocalToWorld {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("Root is null");
                }
                return GetTransformTo (Root);
            }
        }

        /// <summary>
        /// ワールド座標変換行列(法線用)
        /// </summary>
        /// <remarks>
        /// このノードのローカル座標系からワールド座標系に変換する法線用の変換行列です。
        /// このプロパティはノードがシーンに所属しているときだけ有効です。
        /// </remarks> 
        public Matrix3x3 LocalToWorld3x3 {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("Root is null");
                }
                return LocalToWorld.Matrix3x3.Inverse ().Transpose ();
            }
        }

        /// <summary>
        /// カメラ座標変換行列
        /// </summary>
        /// <remarks>
        /// このノードのローカル座標系からカメラの座標系(ビュー座標)へ変換する変換行列を取得します。
        /// このプロパティはノードがシーンに所属し<see cref="World.ActiveCamera"/>がセットされているときのみ有効です。
        /// </remarks>
        public Matrix4x4 LocalToCamera {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("Missing root node");
                }
                if (Root.ActiveCamera == null) {
                    throw new InvalidOperationException ("ActiveCamera is not set");
                }
                return GetTransformTo (Root.ActiveCamera);
            }
        }

        /// <summary>
        /// カメラ座標変換行列(法線用)
        /// </summary>
        /// <remarks>
        /// このノードのローカル座標系からカメラの座標系(ビュー座標)へ変換する法線用の変換行列を取得します。
        /// シーンに所属し<see cref="World.ActiveCamera"/>がセットされているときのみ有効です。
        /// </remarks>
        public Matrix3x3 LocalToCamera3x3 {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("This node is not in the Scene");
                }
                if (Root.ActiveCamera == null) {
                    throw new InvalidOperationException ("ActiveCamera is not set");
                }

                return LocalToCamera.Matrix3x3.Inverse ().Transpose ();
            }
        }

        /// <summary>
        /// 正規化デバイス座標系(NDC)変換行列
        /// </summary>
        /// <remarks>
        /// このノードの座標系から正規化デバイス座標系へ変換する変換行列を取得します。
        /// このプロパティはノードがシーンに所属し<see cref="World.ActiveCamera"/>がセットされているときのみ有効です。
        /// </remarks>
        public Matrix4x4 LocalToProjection {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("This node is not in the Scene");
                }
                if (Root.ActiveCamera == null) {
                    throw new InvalidOperationException ("ActiveCamera is not set");
                }
                var modelView = LocalToCamera;
                var proj = Root.ActiveCamera.As<Camera> ().ProjectionMatrix;

                return proj * modelView;
            }
        }

        /// <summary>
        /// 正規化デバイス座標系(NDC)変換行列(法線用)
        /// </summary>
        /// <remarks>
        /// このノードの座標系から正規化デバイス座標系へ変換する変換行列を取得します。
        /// このプロパティはノードがシーンに所属し<see cref="World.ActiveCamera"/>がセットされているときのみ有効です。
        /// </remarks>
        public Matrix3x3 LocalToProjection3x3 {
            get {
                if (Root == null) {
                    throw new InvalidOperationException ("This node is not in the Scene");
                }
                if (Root.ActiveCamera == null) {
                    throw new InvalidOperationException ("ActiveCamera is not set");
                }
                return LocalToProjection.Matrix3x3.Inverse ().Transpose ();
            }
        }

        /// <summary>
        /// ペアレント・ローカル座標変換行列
        /// </summary>
        public Matrix4x4 ParentToLocal {
            get {
                return LocalToParent.Inverse();
            }
        }

        /// <summary>
        /// ペアレント・ローカル座標変換行列(法線用)
        /// </summary>
        public Matrix3x3 ParentToLocal3x3 {
            get {
                return LocalToParent3x3.Inverse();
            }
        }
        
        
        /// <summary>
        /// ワールド・ローカル座標変換行列
        /// </summary>
        public Matrix4x4 WorldToLocal {
            get {
                return LocalToWorld.Inverse();
            }
        }

        /// <summary>
        /// ワールド・ローカル座標変換行列(法線用)
        /// </summary>
        public Matrix3x3 WorldToLocal3x3 {
            get {
                return LocalToWorld3x3.Inverse();
            }
        }

        /// <summary>
        /// カメラ・ローカル座標変換行列
        /// </summary>
        public Matrix4x4 CameraToLocal {
            get {
                return LocalToCamera.Inverse ();
            }
        }

        /// <summary>
        /// カメラ・ローカル座標変換行列(法線用)
        /// </summary>
        public Matrix3x3 CameraToLocal3x3 {
            get {
                return LocalToCamera3x3.Inverse ();
            }
        }



        /// <summary>
        /// このオブジェクトが参照する全DDDオブジェクトを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// 参照にnullは含まれません。同一のオブジェクトが複数出現する可能性があります。
        /// </remarks>
        public override IEnumerable<Object> References {
            get {
                var objs = base.References.ToList ();
                objs.AddRange (nodes);
                objs.RemoveAll (x=>x==null);
                return objs;
            }
        }
        #endregion

        #region Method
        /// <summary>
        /// 子ノードを追加する
        /// </summary>
        /// <remarks>
        /// このノードの下に子ノードを追加しシーン階層を構築します。
        /// 既に何かの子ノードになっているノードは追加できません。
        /// </remarks>
        /// <param name="node">子ノード</param>
        public void AddChild (Node node) {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            if (node.Parent != null) {
                throw new ArgumentException ("Node is a child of others");
            }
            if (node is World) {
                throw new ArgumentException ("World can't become a child of others");
            }
            if (node == this) {
                throw new ArgumentException ("Can't add yourself as child");
            }

            node.parent = this;
            this.nodes.Add (node);
        }

        /// <summary>
        /// 子ノードを削除します
        /// </summary>
        /// <remarks>
        /// このノードから指定の子ノードを削除します。
        /// 指定の子ノードがこのノードの子ノードでなかった場合単にこれを無視します。
        /// </remarks>
        /// <param name="node">子ノード</param>
        /// <returns>正常に削除されたらtrue, そうでなければfalse</returns>
        public bool RemoveChild (Node node) {
            return this.nodes.Remove (node);
        }


        /// <summary>
        /// 指定のインデックス番号の子ノードを取得します
        /// </summary>
        /// <param name="index">子ノードのインデックス番号</param>
        /// <returns>子ノード</returns>
        public Node GetChild (int index) {
            if (index < 0 || index >= ChildCount) {
                throw new IndexOutOfRangeException ("Index is out of range");
            }

            return nodes[index];
        }

        /// <summary>
        /// 指定ノード座標系への変換行列を取得します
        /// </summary>
        /// <remarks>
        /// このノードの座標系から指定の座標系に変換する変換行列を取得します。
        /// (実装メモ)毎回まじめに計算しているので遅い。フリーズしたシーンでの高速化はよ。
        /// </remarks>
        /// <param name="node">目標座標系のノード</param>
        /// <returns>変換行列</returns>
        public Matrix4x4 GetTransformTo (Node node) {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            var com = GetMostRecentCommonAnscestor (node, this);
            if (com == null) {
                throw new InvalidOperationException ("Can't find transform path");
            }
            var T1 = Upwards.TakeWhile (x => x != com).Aggregate (Matrix4x4.Identity, (sum, y) => y.TRSM * sum);
            var T2 = node.Upwards.TakeWhile (x => x != com).Aggregate (Matrix4x4.Identity, (sum, y) => y.TRSM * sum);

            return T2.Inverse () * T1;
        }


        /// <summary>
        /// 指定されたレイとこのノードのピック処理(交差判定)をとり結果を返します（未実装）
        /// </summary>
        /// <remarks>
        /// レイはワールド座標系で指定します。
        /// このノードのローカル座標系ではありません。
        /// (実装メモ)この館数の戻り値がbool(hit or false)でないのは
        /// LINQ構文ではref,outが使えないため引数で結果を受け取れないから。
        /// 知らなかったなー
        /// </remarks>
        /// <param name="ray">レイ</param>
        /// <returns>交差判定の結果</returns>
        public RayIntersection Pick (Ray ray) {
            throw new NotImplementedException ("Sorry");
        }

        /// <summary>
        /// このノードの-Z軸を目標のノードの方向に向けます
        /// </summary>
        /// <remarks>
        /// このノードの-Z軸(0,0,-1)が指定のノードを向くようにTRSMを変更します。
        /// </remarks>
        /// <param name="node">対象ノード</param>
        /// <param name="upx">UpベクトルのX成分</param>
        /// <param name="upy">UpベクトルのY成分</param>
        /// <param name="upz">UpベクトルのZ成分</param>
        public void LookAt (Node node, float upx, float upy, float upz) {
            if (node == null) {
                throw new ArgumentNullException ("node is null");
            }
            var o = Translation;
            var d = node.Translation;
            base.LookAt (o.X, o.Y, o.Z,
                         d.X, d.Y, d.Z,
                         upx, upy, upz);
        }


        /// <summary>
        ///  2つのノードの共通の親ノードのうち最も近いノードを探します
        /// </summary>
        /// <remarks>
        /// 2つのノードの親ノードで一番近い共通の親ノードを返します。
        /// どちらかのノードがnull又は共通の親ノードが存在しない場合単にnullを返します。
        /// </remarks>
        /// <param name="a">ノードA</param>
        /// <param name="b">ノードB</param>
        /// <returns>共通の親ノード</returns>
        public static Node GetMostRecentCommonAnscestor (Node a, Node b) {
            if (a == null || b == null) {
                return null;
            }

            return a.Upwards.Intersect (b.Upwards).FirstOrDefault ();
        }

        /// <summary>
        /// このノードの複製を返します(未実装)
        /// </summary>
        /// <remarks>
        /// このメソッドは例えばモブキャラを大量生産するのに使用します。
        /// (メモ)ここで定義するかObject, Object3Dあたりで定義するか未だに迷っている
        /// </remarks>
        /// <returns>複製されたノード</returns>
        public Node Duplicate () {
            throw new NotImplementedException ("Sorry");
        }


        /// <summary>
        /// 再帰的に辿れる全ての子ノードのうち<seealso cref="Predicate{T}"/>に一致する子ノードを全て探し出しその列挙子を返します
        /// </summary>
        /// <remarks>
        /// 判定にこのノードは含みません。
        /// </remarks>
        /// <param name="pred">比較に使われる条件式(述語)</param>
        /// <returns>条件に一致したノードを列挙する列挙子</returns>
        public IEnumerable<Node> Search (Predicate<Node> pred) {
            return from node in Downwards
                   where node != this && pred (node)
                   select node;
        }

        /// <summary>
        /// 再帰的に辿れる全ての子ノードのうち指定のｺﾝﾎﾟｰﾈﾝﾄ型を持つものをすべて探しだしその列挙子を返します
        /// </summary>
        /// <remarks>
        /// 判定にこのノードは含みません。
        /// </remarks>
        /// <typeparam name="TComp">比較に使われるｺﾝﾎﾟｰﾈﾝﾄ型</typeparam>
        /// <returns>条件に一致したノードを列挙する列挙子</returns>
        public IEnumerable<Node> Search<TComp> () where TComp : Component {
            return from node in Downwards
                   where node != this && node.Is<TComp> ()
                   select node;
        }

        /// <summary>
        /// 再帰的に全ての子ノードをたどり<see cref="Action{T}"/>を実行します
        /// </summary>
        /// <remarks>
        /// 再帰的に全ての子ノードをたどり指定のアクションを実行します。
        /// アクションとは引数が１つ(ノード)で値を返さない任意の関数の事です。
        /// このノードでは実行されません。
        /// </remarks>
        /// <param name="act">実行するアクション</param>
        public void Take (Action<Node> act) {
            if (act == null) {
                throw new ArgumentNullException ("Action is null");
            }
            foreach (var node in nodes) {
                act (node);
                node.Take (act);
            }
        }

        /// <summary>
        /// 再帰的に全ての子ノードを辿り戻り値がtrueの限り<see cref="Action{T}"/>を実行します
        /// </summary>
        /// <remarks>
        /// 再帰的に全ての子ノードをたどりtrueを返す限り指定のアクションを実行します。
        /// アクションとは引数が１つ(ノード)でbool値を返す任意の関数の事です。
        /// このノードでは実行されません。
        /// </remarks>
        /// <param name="act">アクション</param>
        public void Take (Func<Node, bool> act) {
            if (act == null) {
                throw new ArgumentNullException ("Action is null");
            }
            foreach (var node in nodes) {
                if (act (node)) {
                    node.Take (act);
                }
            }
        }


        /// <summary>
        /// 再帰的に辿れる全ての子ノードのうち<seealso cref="Predicate{T}"/>が一致するものに対して
        /// <seealso cref="Action{T}"/>を実行します
        /// </summary>
        /// <remarks>
        /// このノードから再帰的に辿れる子ノードのうち指定の述語(<seealso cref="Predicate{T}"/>)がtrueの
        /// ノードにたいしてアクション(<seealso cref="Action{T}"/>)を実行します。
        /// このノードは含みません。
        /// </remarks>
        /// <param name="pred">述語（条件式)</param>
        /// <param name="act">実行するアクション</param>
        public void Take (Predicate<Node> pred, Action<Node> act) {
            if (pred == null) {
                throw new ArgumentNullException ("Predicate is null");
            }
            if (act == null) {
                throw new ArgumentNullException ("Action is null");
            }

            var nodes = from node in Downwards
                        where node != this && pred (node)
                        select node;
            foreach (var node in nodes) {
                act (node);
            }
        }

        /// <summary>
        /// 再帰的に辿れる子ノードのうち指定されたコンポーネント型を持つもの全てに対して
        /// <seealso cref="Action{T}"/>を実行します
        /// </summary>
        /// <remarks>
        /// 再帰的に辿れる子ノードで指定のコンポーネント型を持つ物に対してアクションを実行します。
        /// このノードは含みません。
        /// </remarks>
        /// <typeparam name="TComp">コンポーネント型</typeparam>
        /// <param name="act">実行するアクション</param>
        public void Take<TComp> (Action<Node> act) where TComp : Component {
            if (act == null) {
                throw new ArgumentNullException ("Action is null");
            }

            var nodes = from node in Downwards
                        where node != this && node.Is<TComp> ()
                        select node;
            foreach (var node in nodes) {
                act (node);
            }
        }

        /// <summary>
        /// アライメントを設定します(未実装)
        /// </summary>
        /// <remarks>
        /// 指定のノードに自動的に軸をそろえるようにアライメントを設定します。
        /// アライメントはZ軸とY軸を別々に設定可能です。
        /// <see cref="World.Align"/>を呼び出すとアラインします。
        /// </remarks>
        /// <param name="zTarget">Z軸ターゲット</param>
        /// <param name="yTarget">Y軸ターゲット</param>
        public void SetAlignment (Node zTarget, Node yTarget) {
            throw new NotImplementedException ("Sorry");
        }

        /// <summary>
        /// 現在のオブジェクトの状態を表す文字列を返します。
        /// </summary>
        /// <returns>現在のオブジェクトを説明する文字列</returns>
        public override string ToString () {
            return string.Format ("Node: Children={0}", ChildCount);
        }
        #endregion

    }
}
